/*******************************************************************************
 * (c) Copyright 2017 lvxi. All Rights Reserved.
 ******************************************************************************/

package edu.jxufe.nb112.common.code.codedc;

import edu.jxufe.nb112.common.code.exception.CommonCodeException;
import org.apache.commons.text.RandomStringGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by lvxi on 2017/4/16.
 * Note:password是16位的
 */
public class AESUtils {
    private static final Logger LOG = LoggerFactory.getLogger(AESUtils.class.getName());
    private final static Pattern timePattern = Pattern.compile("(?<=^\\[)[\\d\\|]*(?=\\])");
    private final static String password;

    static {
        //生成默认随机密码
        RandomStringGenerator generator = new RandomStringGenerator.Builder().withinRange('a', 'z').build();
        password = generator.generate(16);
        if (LOG.isDebugEnabled()) {
            LOG.debug("AESUtils随机秘钥:{}", password);
        }
    }

    /**
     * 使用默认随机密码加密
     *
     * @param in 需要加密字符
     * @return
     */
    public static String encode(String in) {
        return encode(in, password);
    }

    /**
     * 加密
     *
     * @param in       需要加密字符
     * @param password 秘钥 16位
     * @return
     */
    public static String encode(String in, String password) {
        String hex = "";
        try {
            byte[] bytIn = in.getBytes("utf-8");

            SecretKeySpec skeySpec = new SecretKeySpec(password.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            byte[] bytOut = cipher.doFinal(bytIn);
            hex = HexUtils.byte2hexString(bytOut);
        } catch (Exception e) {
            LOG.error("AES加密错误", e);
            throw new CommonCodeException("AES加密错误", e);
        }
        return hex;
    }

    /**
     * 使用默认秘钥解密
     *
     * @param hex
     * @return
     */
    public static String decode(String hex) {
        return decode(hex, password);
    }

    /**
     * 解密
     *
     * @param hex      密文
     * @param password 秘钥 16位
     * @return
     */
    public static String decode(String hex, String password) {
        String rr = "";
        byte[] bytIn = HexUtils.hex2Byte(hex);
        SecretKeySpec skeySpec = new SecretKeySpec(password.getBytes(), "AES");
        try {
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] bytOut = cipher.doFinal(bytIn);
            rr = new String(bytOut, "utf-8");
        } catch (Exception e) {
            LOG.error("AES解密错误", e);
            throw new CommonCodeException("AES解密错误", e);
        }
        return rr;
    }

    /**
     * 带时间戳加密,在liveTime毫秒后失效
     *
     * @param in
     * @param liveTime 有效时间 毫秒
     * @return
     */
    public static String encodeTimestap(String in, long liveTime) {
        return encodeTimestap(in, liveTime, password);
    }

    /**
     * 加密,在liveTime毫秒后失效
     *
     * @param in
     * @param liveTime 有效时间 毫秒
     * @param password
     * @return
     */
    public static String encodeTimestap(String in, long liveTime, String password) {
        String timeInfo = System.currentTimeMillis()/60000 + "|" + liveTime;
        in = "[" + timeInfo + "]" + in;
        return encode(in, password);
    }

    /**
     * 带时间戳解密
     * @param hex
     * @return
     */
    public static String decodeTimestap(String hex){
        return decodeTimestap(hex,password);
    }
    /**
     * 带时间戳解密
     * @param hex
     * @param password
     * @return
     */
    public static String decodeTimestap(String hex, String password){
        String inStr = decode(hex,password);
        Matcher matcher = timePattern.matcher(inStr);
        if(matcher.find()){
            String detimeStr = matcher.group();
            String[] destime = detimeStr.split("\\|");
            long time = Long.parseLong(destime[0]);
            long liveTime = Long.parseLong(destime[1]);
            if(System.currentTimeMillis()/60000-time <= liveTime) {
                return inStr.substring(detimeStr.length() + 2);
            }
        }
        return null;
    }
}
